package concurrent;

import java.util.concurrent.Semaphore;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 多线程交替打印
 * 题目描述：建立三个线程A、B、C，A线程打印10次字母A，B线程打印10次字母B,C线程打印10次字母C，但是要求三个线程同时运行，并且实现交替打印，即按照ABCABCABC的顺序打印。
 */
public class ABCPrint {
    public static void main(String[] args) {
//        ABC_Condition abc_condition = new ABC_Condition();
//        abc_condition.printing();
//        ABC_Semaphore abc_semaphore = new ABC_Semaphore();
//        abc_semaphore.printing();
//        ABC_Lock abc_lock = new ABC_Lock();
//        abc_lock.printing();
        ABC_Sync abc_sync = new ABC_Sync();
        abc_sync.printing();
    }
}
/**
 * synchronize同步，需要保证初始有序，因为初始资源都是齐全的，任何一个都可以跑起来
 * wait+notify
 */
class ABC_Sync{
    class ThreadPrinter implements Runnable{
        private String name;
        private Object prev;
        private Object self;

        private ThreadPrinter(String name, Object prev, Object self) {
            this.name = name;
            this.prev = prev;
            this.self = self;
        }

        @Override
        public void run() {
            int count = 10;
            while(count>0){// 多线程并发，不能用if，必须使用whil循环
                synchronized (prev){// 先获取 prev 锁
                    synchronized (self){// 再获取 self 锁
                        System.out.print(name);//打印
                        count--;
                        self.notifyAll();// 唤醒其他线程竞争self锁，注意此时self锁并未立即释放。
                    }
                    //此时执行完self的同步块，这时self锁才释放。
                    try {
                        if(count > 0){
                            prev.wait();// 立即释放 prev锁，当前线程休眠，等待唤醒，如果还有次数，需要阻塞等待在此处
                        }else{
                            prev.notifyAll(); // 如果打印次数足够，那么直接唤醒所有,不再等待prev，当前线程结束
                        }

                        /**
                         * JVM会在wait()对象锁的线程中随机选取一线程，赋予其对象锁，唤醒线程，继续执行。
                         */
                    }catch (InterruptedException e){
                        e.printStackTrace();
                    }
                }
            }

        }
    }

    public void printing(){
        Object a = new Object();
        Object b = new Object();
        Object c = new Object();

        ThreadPrinter pa = new ThreadPrinter("A", c, a); // 当前锁是a，先前的锁是c
        ThreadPrinter pb = new ThreadPrinter("B", a, b);
        ThreadPrinter pc = new ThreadPrinter("C", b, c);

        try {
            new Thread(pa).start();
            Thread.sleep(10);//保证初始ABC的启动顺序
            new Thread(pb).start();
            Thread.sleep(10);
            new Thread(pc).start();
            Thread.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

/**
 * Lock锁方法
 */
class ABC_Lock{
    private Lock lock = new ReentrantLock();
    private int state = 0;

    class ThreadA implements Runnable{
        @Override
        public void run() {
            for (int i = 0; i < 10;) {
                try {
                    lock.lock();
                    while (state % 3 == 0) {// 多线程并发，不能用if，必须用循环测试等待条件，避免虚假唤醒
                        System.out.print("A");
                        state++;
                        i++;
                    }
                } finally {
                    lock.unlock();// unlock()操作必须放在finally块中
                }
            }
        }
    }

    class ThreadB implements Runnable{
        @Override
        public void run() {
            for (int i = 0; i < 10;) {
                try {
                    lock.lock();
                    while (state % 3 == 1) {// 多线程并发，不能用if，必须用循环测试等待条件，避免虚假唤醒
                        System.out.print("B");
                        state++;
                        i++;
                    }
                } finally {
                    lock.unlock();// unlock()操作必须放在finally块中
                }
            }
        }
    }

    class ThreadC implements Runnable{
        @Override
        public void run() {
            for (int i = 0; i < 10;) {
                try {
                    lock.lock();
                    while (state % 3 == 2) {// 多线程并发，不能用if，必须用循环测试等待条件，避免虚假唤醒
                        System.out.print("C");
                        state++;
                        i++;
                    }
                } finally {
                    lock.unlock();// unlock()操作必须放在finally块中
                }
            }
        }
    }
    public void printing(){
        new Thread(new ThreadA()).start();
        new Thread(new ThreadB()).start();
        new Thread(new ThreadC()).start();
    }
}

/**
 * ReentrantLock+Condition，await和signal
 */
class ABC_Condition{
    private Lock lock = new ReentrantLock();
    private Condition A = lock.newCondition(); // lock才能创建Condition
    private Condition B = lock.newCondition();
    private Condition C = lock.newCondition();

    private int count = 0;

    class ThreadA implements Runnable{
        @Override
        public void run() {
            try{
                lock.lock();
                for (int i = 0; i < 10; i++) {
                    while (count%3!=0){
                        A.await();  // 不为0，阻塞,一直等着需要他人唤醒,该线程释放自己的锁，其他线程可以拿到，所以一开始三个都lock.lock（）
                                    // 下次signal之后从此处开始接着往后
                    }
                    System.out.print("A");
                    count++;
                    B.signal();
                }
            }catch (InterruptedException e){
                e.printStackTrace();
            }finally {
                lock.unlock();
            }
        }
    }

    class ThreadB implements Runnable{
        @Override
        public void run() {
            try{
                lock.lock();
                for (int i = 0; i < 10; i++) {
                    while (count%3!=1){
                        B.await();  // 不为1，阻塞,一直等着需要他人唤醒
                    }
                    System.out.print("B");
                    count++;
                    C.signal();
                }
            }catch (InterruptedException e){
                e.printStackTrace();
            }finally {
                lock.unlock();
            }
        }
    }

    class ThreadC implements Runnable{
        @Override
        public void run() {
            try{
                lock.lock();
                for (int i = 0; i < 10; i++) {
                    while (count%3!=2){
                        C.await();  // 不为2，阻塞,一直等着需要他人唤醒
                    }
                    System.out.print("C");
                    count++;
                    A.signal();
                }
            }catch (InterruptedException e){
                e.printStackTrace();
            }finally {
                lock.unlock();
            }
        }
    }

    public void printing(){
        new Thread(new ThreadA()).start();
        new Thread(new ThreadB()).start();
        new Thread(new ThreadC()).start();
    }
}

/**
 * Semaphore信号量方式，初始只有A有1个资源，通过资源的+1-1实现
 * acquire-1，release+1，不用显示lock
 */
class ABC_Semaphore{
    // 以A开始的信号量,初始信号量数量为1
    private Semaphore A = new Semaphore(1); // 工具类都可以直接创建
    // B、C信号量,A完成后开始,初始信号数量为0
    private Semaphore B = new Semaphore(0);
    private Semaphore C = new Semaphore(0);

    class ThreadA implements Runnable{
        @Override
        public void run() {
            try {
                for (int i = 0; i < 10; i++) {
                    A.acquire();// A获取信号执行,A信号量减1,当A为0时将无法继续获得该信号量,A信号量无了就在此阻塞
                    System.out.print("A"); // 放在acquire到的后面
                    B.release(); // B信号量+1
                }
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }
    }
    class ThreadB implements Runnable{
        @Override
        public void run() {
            try {
                for (int i = 0; i < 10; i++) {
                    B.acquire();// B获取信号执行,B信号量减1,当B为0时将无法继续获得该信号量,B信号量无了就在此阻塞
                    System.out.print("B");
                    C.release(); // C信号量+1
                }
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }
    }
    class ThreadC implements Runnable{
        @Override
        public void run() {
            try {
                for (int i = 0; i < 10; i++) {
                    C.acquire();// C获取信号执行,C信号量减1,当C为0时将无法继续获得该信号量,C信号量无了就在此阻塞
                    System.out.print("C");
                    A.release(); // A信号量+1
                }
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }
    }

    public void printing(){
        new Thread(new ThreadA()).start();
        new Thread(new ThreadB()).start();
        new Thread(new ThreadC()).start();
    }
}